##12345 #####################################

#stringsIntro
#Strings Intro Mar10 PanosP

#Introduction to Strings 

#Strings in awk are any sequences of characters of any kind: letters,
#digits, symbols, control characters, blanks etc. There are string
#literals and variables. String literals are character sequences 
#enclosed in double quotes, e.g. "panos", "abc123", "this is line
#one\nthis is line 2" etc. String variables are variables that contain
#strings. Even number variables may be handled as strings, it's just
#a matter of concept. Let's look some examples:

#  x = "abc"

#Defines the variable named x having the string "abc" as a value.
#That means that if we print x, the "abc" character sequence will
#be printed (without the quotes, of course).

#  x = 123

#Defines the variable x having the number 123 as a value. But we can
#use the x variable as a string, e.g.

#  y = "panos" x "papadopoulos"

#will put the string "panos123papadopoulos" in variable y. That is
#a consequence of the string concatenation in awk which is achieved
#by just puting the strings to concatenate one after the other,
#without any kind of extra operator (very easy). So, if, by mistake,
#we had writen:

#  y = "panos" + x + "papadopoulos"

#then y would have taken the number value of 123, as "panos" will
#be "added" with the (number) 123 and the result will be added with
#the string "papadopoulos" taken as a number. Both "panos" and
#"papadopoulos" will give the value of zero if taken as numbers.
#Every string not begining with digits (with the exception of white
#characters) will have the numeric value of zero if taken as a number.
#So:

#+  "panos123" have the number value of zero, 
#+ "123panos" have the number value of 123, 
#+ "123.23panos" have the number value of 123.23
#+ "   123panos" (with three spaces in front of 1) will have the number value of 123.

 function StringsDemo(   x, y1, y2, z1, z2, z3, z4) {
 	x = "abc"
 	x = "123"
 	y1= "panos" x "papadoupoulous"
 	y2= "panos" + x + "papadopoulos"
 	z1= "panos123" + 1
 	z2= "123paons" + 1
 	z3= "123.23panos" + 1
 	z4= "   123panos" + 1
 	print("x=["x"] y1=["y1"] z1=["z1"] z2=["z2"] z3=["z3"] z4=["z4"]")
 }   

#There is a small suite of library functions for handling and
#manipulating strings in awk. These functions are not so many as in
#other languages, e.g. PHP, or Java, but you can easily construct
#your own functions using these primitive functions.

##12345 #####################################

#trimmingBlanks
#Strings Sandbox PanosP

#Trimming Blanks from a String 

#Problem
#=======

#Remove leading, trailing blanks from a string.

#Solution
#========

#Let's begin with a basic  function to trim "white" characters
#from both the begining and the end of a given string:

 function trim(s) {
 	sub(/^[ \t\r\n]+/, "", s)
 	sub(/[ \t\r\n]+$/, "", s)
 	return(s)
 }

#Simple enough. Now, let's add a second argument to specify the edge
#where the white caracters will be trimmed. That argument will be
#optional, defaulting to the  right edge (as this is often the wanted
#action).  We will specify the edge(s) using some letters as mnemonics.

#  #version1: we'll do it better, below
#  function trimStr(s, lrb) {
#  	lrb = lrb ? lrb : "r"
#  	if (lrb ~ /[lLbB]/) sub(/^[ \t\r\n]+/, "", s)
#  	if (lrb ~ /[rRbB]/) sub(/[ \t\r\n]+$/, "", s)
#  	return(s)
#  } 

#The second argument (_lrb_) is optional. If we call this function
#with only one argument, the second argument _lrb_ will be assigned
#the empty string value.  If we have an empty (missing) _lrb_ argument,
#we assume the default, that is trim only at the right edge (end) of the
#given string (i.e. set it to "r").

#(BTW, recall that all the function arguments are copied
#in new, local (static) variables inside the function's scope, when
#the function is called. So, setting the _lrb_ value to "r" inside the
#function doesn't affect any variable outside the function's scope.)

#Using these optional arguments as control variables is handy,
#but there are some problems. First of
#all, let's consider the dangerous situation where we pass an erroneus
#_lrb_, e.g. "e" believing it's "r" (fat fingering). With a little
#cost we can catch that kind of errors:

#  #version2
#  function trimStr(s, lrb) {
# 	ok(lrb ~ /[rRlLbB]/, "trimStr: ["lrb"] bad lrb arg",2)
# 	lrb = lrb ? lrb : "r"
# 	if (lrb ~ /[lLbB]/) sub(/^[ \t\r\n]+/, "", s)
# 	if (lrb ~ /[rRbB]/) sub(/[ \t\r\n]+$/, "", s)
# 	return(s)
#  }

#We begin by checking the _lrb_ argument not to contain anything else
#but the valid characters. If anything else matches, then we abort
#the whole program, because that's a serious program error and must
#be fixed. After that check, we can check for empty _lrb_ value by
#checking _(!lrb)_ instead of the less elegant _(lrb != "")_, because
#we alredy have checked the case of passing a "0" _lrb_ value by
#mistake.

#As a last step in constructing the final version of out _trimStr_
#function, we provide the means for specifying what we mean as "white"
#characters. By default we consider the spaces, tabs, carriage returns
#and newlines as "white" characters. But we can also provide some
#extra "white" characters if needed, e.g. vertical characters or
#form feeds. To achieve such a goal, we can provide a third (optional)
#argument to our _trimStr_ function which, if given, will specify the
#"white" characters as a string, e.g. if we want to trim just spaces
#and tabs, we can provide the _" \t"_ string:

#  #version3
#  function trimStr(s, lrb, wcs) {
#  	ok(lrb ~ /[rRlLbB]/, "trimStr: ["lrb"] bad lrb arg",2)
#  	lrb = lrb ? lrb : "r"
#  	wcs = wcs ? wcs : " \t\r\n"
#  	if (lrb ~ /[lLbB]/) sub("^[" wcs "]+", "", s)
#  	if (lrb ~ /[rRbB]/) sub("[" wcs "]+$", "", s)
#  	return(s)
#  } 

#Finally we came up with a small but robust function for trimming
#strings. By the way, did you notice the use of dynamic regular
#expressions in _sub_ calls? Beyond of robust, our little function
#proves to be flexible too. Actually, we can trim characters of any
#kind, not just "white" characters, using the _wcs_ argument. If, let's
#say, we want to trim any non letter or underscore characters from
#the begining of a string, we can just call:

#  trimStr(s, "l", "^a-zA-Z_")

#In order to make the function even more flexible, we can allow the _wcs_
#argument to be a regular expression rather than a plain string.
#Doing so, we can provide more complex strings to be trimmed out
#from the string edges. The cost is that in the (rare) case where
#we call the function to just trim some extra "white" characters,
#we have to provide the brackets and the plus symbol too
#(remind that the plus symbol is used for repetetion in RE);
#but, I believe, that's a
#little cost for such a great flexibility! So here, at last, is the
#final version:

 # final version
 function trimStr(s, lrb, wcre) {
 	ok(lrb ~ /[rRlLbB]/, "trimStr: ["lrb"] bad lrb arg",2)
 	lrb  = lrb  ? lrb  : "r"
 	wcre = wcre ? wcre : "[ \t\r\n]+"
 	if (lrb ~ /[lLbB]/) sub("^" wcre, "", s)
 	if (lrb ~ /[rRbB]/) sub(wcre "$", "", s)
 	return(s)
 }

#The following program will print all strings passed in the input
#stream, after cutting out any non letter or underscore characters
#from both string edges. The printed strings are printed one string
#per line:

 function TrimStr(    x,y,tmp) {	
 	split("  _ 1 2 3 ,arthur", tmp, ",")
 	for(x in tmp) {
 		if (y = trimStr(x, "b", "[^a-zA-Z_]+"))
 			print y
 	}
 }

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#deleteArray
#Arrays Sandbox PanosP

#Deleting Elements from an Array

#Problem
#=======

#Is there a way to delete (remove) array elements, or arrays as a whole?

#Solution
#========

#You can delete any element of an array if, of course, you know the
#element's index (key), e.g. to delete the `"panos"` indexed element
#of the array `users`:

#  delete users["panos"]

#There's no problem trying to delete an element that doesn't exist,
#or even trying to delete an "element" of a non existent array,
#but, if this is the case, the name of the array cannot longer be
#used for any scalar or name of a function.

#To delete the array `users` as a whole, just:

#  delete users

#that is delete the array without specify an index.

#Discussion
#==========

#Scope of Array Names
#--------------------

#After deleting an array as a whole,
#you cannot use the same name for a scalar variable;
#the name has been "marked" as an array name by awk (permanently), so
#you can only use that name for a new array, nothing else.
#If you delete an array that doesn't even exist, then
#the name specified is "remembered" by awk as an array,
#you can't use that name for anything else but an array.

#Delete First, Then Add New Entries
#----------------------------------

#When constructing functions that fill array elements, e.g. the
#`split` builtin awk function, then don't forget to
#delete the array before starting indexing new array elements.
#If the array passed contains any elements
#the time it's passed to the function, then these elements will,
#of course, remain as array elements, mixed with the
#new elements added by the function. Let's make that
#point clear using an example. The code below is
#a function that accepts a user name and fills
#an array with information about the user just
#taken from `/etc/passwd` file. The indices to
#be used are:

#+ uid (user id, the 3rd field)
#+ gid (group id, the 4rd field)
#+ info (full name, or other information, the 5th field)
#+ home (home directory, the 6th field)
#+ shell (shell program, the 7th field)

 function get_user_info(name, data,		ufile, ofs, found) {
 	delete data	# don't forget
 	found = 0
 	ufile = "/etc/passwd"
 	ofs = FS
 	FS = ":"
 	while ((getline <ufile) > 0) {
 		if ($1 == name) {
 			data["uid"] = $3
 			data["gid"] = $4
 			data["info"] = $5
 			data["home"] = $6
 			data["shell"] = $7
 			found = 1
 			break
 		}
 	}
 	close(ufile)
 	FS = ofs
 	return(found)
 }

#Failing to call `delete data` in the beging of the funtion, the `data`
#array may have elements already filled by previous calls to that same
#(or other) function. Of course, you can check the returned value
#(`found`) to check when the user were found or not in the password file,
#but it's very risky to leave such arrays undeleted before start filling
#them up.

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#printArrayCsv
#Arrays Sandbox PanosP

#Printing an Array with Commas

#Problem
#=======

#Print all array elements as a comma separated list.
#We can print a number indexed array in an ordered
#fashion, or print all elements of an associative
#array in a random order.

#Solution
#========

 function csvArray(arr, from, to,		i, first) {
 	if (from != "") {
 		printf arr[from]
 		for (i = from + 1; i <= to; i++)
 			printf "," arr[i]
 	}
 	else {
 		first = 1
 		for (i in arr) {
 			if (first) {
 				printf arr[i]
 				first = 0
 			}
 			else
 				printf "," arr[i]
 		}
 	}
 	print ""
 }

#The above function takes at most three arguments.
#The first argument is, of course, the array to print.
#That's a mandatory argument.
#The other two arguments are optional, either none
#or both of the `from` and `to` arguments must
#be supplied; if supplied, then both `from`
#and `to` must be numbers that specify a range of indices
#of the corresponding elements of the array to print
#as a comma separated list.
#If the `from` and `to` arguments are missing,
#then all of the array elements will be printed
#(in random order) as a comma separated list.

#Discussion
#==========

#In case of an empty array the above function may print
#just a newline an nothing else.
#In case of ordered print (specified `from` and `to`
#arguments), if some of the indices are missing, then:

#+ missing elements will be printed as empty strings.
#+ missing elements will be inserted in the array, because
#just the reference of an element using a specific index
#will cause the insertion of the corresponding element
#if it doesn't exist already (a well known sideffect of
#arrays in gawk, useful most of the times).

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#checkArrayIdx
#Arrays Sandbox PanosP

#Checking if a Key Is in an Array

#Problem
#=======

#We want to check if a given "key" is indexing
#some array element in a given array.

#Solution
#========

#Operator `in` just do the job in awk:

#  ...
#  if (x in a)
#  	# x indexed element exists.
#  else
#  	# x indexed element doesn't exist.

#To make a function for that simple check
#is just a formal matter:

 function arrayKeyExists(arr, idx) {
 	return(idx in arr)
 }

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#checkArrayVal
#Arrays Sandbox PanosP

#Checking if an Element Is in an Array

#Problem
#=======

#We want to check if a given value exists
#as some element's value in a given array.

#Solution
#========

#It's not as simple as checking for key
#existence. We must iterate the array elements
#to tell if a given string exists as a given array
#element's value:

#  function arrayValueExists(arr, val,		i) {
#  	for (i in arr) {
#  		if (val == arr[i])
#  			return(1)
#  	}
#  	return(0)
#  }

#Discussion
#==========

#Function returns zero if the given value is not
#an element of the given array. In that case *all*
#of the array elements must be visited to tell so.
#In the contrary, if the value exists, then the
#function returns a non zero value (1) just
#when the corresponding element is visited.
#It would be of much help to return the
#index of the value found, but in that case
#we must take care of what to return if the value
#doesn't exist. However, it's a simple thing
#to implement such an approach, e.g.
#given an array where there is not an empty string
#indexed element, the function would look like:

 function arrayValueExists(arr, val,		i) {
  	for (i in arr) {
  		if (val == arr[i])
  			return(i)
  	}
  	return("")
 }

#Keep in mind, that there may exist more than one
#elements with the same value. As a last step in
#this topic, we'll introduce a function to return
#the _number of occurences_ of the given value,
#instead of just telling if the value exists:

 function arrayValueOccurences(arr, val,	i, n) {
 	for (i in arr) {
 		if (val == arr[i])
 			n++
 	}
 	return(n + 0)
 }

#The above function returns the number of elements
#of the specified value, or zero when no such elements
#exist in the array. Note, however, that all of the
#elements must be visited.

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#hiloArray
#Arrays Sandbox PanosP

#Finding the Largest or Smallest Valued Element in an Array

#Problem
#=======

#Given an array, find the largest or smallest valued element.

#Solution
#========

#First we'll find the largest element of an array containig numbers.
#We must be careful not to let awk compare the array elements as
#strings, because 2 will be considered greater than 10 if compared
#as strings.

 function findMaxNum(arr,			i, max) {
 	for (i in arr) {
 		if ((arr[i] + 0) > (max + 0))
 			max = arr[i]
 	}
 	return(max)
 }

#To find the smallest number valued element is similar:

 function findMinNum(arr,			i, min) {
 	for (i in arr) {
 		if ((arr[i] + 0) < (min + 0))
 			min = arr[i]
 	}
 	return(min)
 }

#None of the above functions behave well when passed
#an empty array. Also, array elements that are not pure numbers,
#e.g. "123abc", may produce problems or erroneus results. The
#only precaution taken by the functions above is to add number
#zero to both array elements and to current max or min items
#to enforce number comparison.

#Now let's develop a more sophisticated function to seek
#the "largest" or "smallest" value of arrays of any type.
#Assuming array values are in the form of _lastname_:_firstname_:_nsn_,
#e.g. `Papadopoulos:Panos:032792320`, `Menzies:Tim:782445800`, etc.
#We want the largest element based on `nsn` (national security number).
#It would be nice to provide a _comparison_ function as an argument.
#That function may take as arguments the array and two
#indices to compare the correspondig elements, and return -1, 0,
#or 1 when the first element is "lower" than the second,
#0 if the two elements are of the same value, and 1
#if the first element is greater than the second:

 function findMax(arr, cmp,		i, maxi) {
 	for (i in arr) {
 		if (maxi == "")
 			maxi = i
 		else if (cmp(arr, i, maxi) > 0)
 			maxi = i
 	}
 	return(maxi)
 }

 function cmp(arr, i, j,			a, ai) {
 	split(arr[i], ":", a)
 	ai = a[3]
 	split(arr[j], ":", a)
 	if (ai < a[3])
 		return(-1)
 	if (ai > a[3])
 		return(1)
 	return(0)
  }

#Arnold Robins, the main gawk maintainer,
#is working on that issue
#([?http://www.eggheadcafe.com/software/aspnet/35699599/indirect-function-calls-p.aspx]),
#but for the time being *there is no such thing as function pointers or so in awk*.
#So we must supply the comparison procedure as
#part of the main function:

#  function findMaxPerson(arr,	i, maxi, maxssn, a) {
#  	for (i in arr) {
#  		split(arr[i], ":", a)
#  		if (maxi == "") {
#  			maxssn = a[3]
#  			maxi = i
#  		}
#  		else if (a[3] > maxssn) {
#  			maxssn = a[3]
#  			maxi = i
#  		}
#  	}
#  	return(maxi)
#   }

#Discussion
#==========

#We must be careful for arrays with empty indexed items.
#If that's the case, then the `(maxi == "")` check must change.

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#reverseArray
#Arrays Sandbox PanosP

#Reversing an Array

#Problem
#=======

#Given an array, reverse the order of the elements, assuming
#that array is number indexed with step of one.

#Solution
#========

#Synopsis
#--------

#  reverseArray(arr	[, from [, to]])

#Reverse the order of elements in a number indexed array.
#Array is passed as the first argument to the function.
#Array limits (the lower and higher indices) are passed
#as second and third arguments. If not passed (empty)
#the second argument, assume the lower index to be zero.
#If not passed (empty) the third argument, assume the
#high index to be the last index found in the array
#steping one by one _after_ the low index.
#If any of the indices passed is invalid or out
#of range, then the function prints a message an exits
#with a non zero exit status.

#Code
#----

 function reverseArray(arr, from, to,		errs, x, count) {
 	if (from == "")
 		from = 0
 	else if (from ~ /^[ \t]*[0-9]+[ \t]*$/)
 		from += 0
 	else
 		errs += warning("reverseArray: invalid `from' " \
			"index (" from ")")
 	if (to ~ /^[ \t]*[0-9]+[ \t]*$/)
 		to += 0
 	else if (to != "")
 		errs += warning("reverseArray: invalid `to' " \
			"index (" to ")")
 	notOk(errs)
 	if (!(from in arr))
 		errs += barph("reverseArray: `from' " \
			"index out of range (" from ")")
 	# If not given the upper index, the seek it stepping one
 	# by one after the low index. If there are gaps in the
 	# array (some indices are missing) the the elements after
 	# the first gap will be ignored!
 	if (to == "") {
 		for (to = from + 1; to in arr; to++)
 			;
 		to--
 	}
 	# Normally, this is the point to check for the existence
 	# of the `to' index. But we leave that free in order to
 	# give the flexibility to create new elements as needed,
 	# e.g. given the array a[1], a[2], a[3] we can call
 	# reverseArray(a, 1, 10) and produce a[1]...a[10], puting
 	# a[1] to a[10], a[2] to a[9] and a[3] to a[8].
 	if (to < from)
 		errs += barph("reverseArray: invalid index range (" \
 			from " - " to ")")
 	for (count = 0; from < to; count++) {
 		x = arr[from]
 		arr[from] = arr[to]
 		arr[to] = x
 		from++
 		to--
 	}
 	return(count)
 }

#Return Value
#------------

#The function returns the number of switched elements.

#Discussion
#==========

#The function gives a lot of freedom in the calling phace.
#However, the most frequently form of calling the function
#may be the:

#  reverseArray(x, 1)

#or something like that. I believe it's a good practice to
#give both the lower and upper indices, but that's a matter
#of taste. Another point to ponder:
#Given the upper index, missing elements will
#be created!

#Of course, the indices given may reverse only part
#of the array, e.g.

 function TestRevArr(			a, i) {
 	# Create an array of 20 elements indexed zero
 	# based.
 	for (i = 0; i < 10; i++)
 		a[i] = "element_" i
 	# Reverse only the elements 3 throuh 7.
 	reverseArray(a, 3, 7)
 	for (i = 0; i < 10; i++)
 		print i ": " a[i]
 }

#will print:

#  0: element_0
#  1: element_1
#  2: element_2
#  7: element_7
#  6: element_6
#  5: element_5
#  4: element_4
#  3: element_3
#  8: element_8
#  9: element_9

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#shuffleDeck
#Arrays Sandbox PanosP

#Shuffling a Deck of Cards

#Problem
#=======

#Assuming we have an array containing a deck of cards, we want
#to shuffle the deck, that is rearange the array items in a random order.

#Solution
#========

#We'll develop a simple function and pass the _deck_ (the array of "cards")
#as the first parameter to that function.
#The cards' array (_deck_) is a number indexed, 0 based (0, 1, 2,...)
#array, while the cards' _count_ (how many cards are in the deck)
#will be passed to the function as a second parameter
#for more flexibility.
#So, having a classic deck of 52 cards in array `deck`, we'll call
#the function as:

#  shuffleDeck(deck, 52)

#After calling the function, the `deck` array must have been shuffled
#and ready to deal. Here is the code:

 function shuffleDeck(deck, count,		i, n, card) {
 	seed(1) 
 	for (i = 0; i < count; i++) {
 		n = int(rand() * count)
 		card = deck[i]
 		deck[i] = deck[n]
 		deck[n] = card
 	}
 }

#That's all, folks!

#Shuffling a Deck of Cards in Action
#-----------------------------------

#Here is a complete program demonstrating the `shuffleDec` function:

 function ShuffleDeck52(		deck) {
 	newDeck52(deck)
 	shuffleDeck(deck, 52)
 	dealDeck(deck, 52)
 }

#Create a new deck. 52 cards will be placed in that new
#deck, not shuffled by any means. Instead, the cards will
#be ordered by suit and order (Ace, 2, 3,... 10, J, Q, K).
#The order of suits is undefined.

 function newDeck52(deck,	suits, figure, suit, i, n) {
	delete deck
 	suits["spades"]
 	suits["clubs"]
 	suits["diamonds"]
 	suits["hearts"]
	for (i = 2; i <= 10; i++)
 		figure[i] = i
 	figure[1] = "A"
 	figure[11] = "J"
 	figure[12] = "Q"
 	figure[13] = "K"
 	for (suit in suits) {
 		for (i = 1; i <= 13; i++)
 			deck[n++] = figure[i] " of " suit
 	}
 }

 function dealDeck(deck, count,		i) {
 	for (i = 0; i < count; i++)
 		print deck[i]
 }

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#staticVars
#Variables Sandbox PanosP

#Using Static Variables

#Problem
#=======

#Can we use variables with limited scope in awk?
#How can we hide a variable inside a function
#or any other program block?

#Solution
#========

#In awk there is no need to declare or define
#a variable before using it. Actually, all variables
#are automatically come into existence the first time we use them.
#Even if we wish to declare or define the variables
#to use, there are no keywords such as _int_, _char_,
#_float_ as in C, or Java, to do such a thing.
#At first glance, that seems to be a disadvantage for awk,
#because variables used in all functions, or other
#program portions, e.g. BEGIN and END actions,
#share a global name scope, that is every variable used
#anywhere in the program is globaly known. Surely,
#that's not a good thing for a language; unwanted globalness
#is the origin of all evil in software.
#Do we have a major problem then? The answer is:
#Of course, no!

#But how can we make a variable "local",
#or, using some C or Java terminology,
#"static" to some portion of a program?
#The truth is that there is an elegant,
#indirect way to declare variables
#to be "local" in awk functions.
#We know that every time we call
#an awk function, all the values of the
#parameters passed as arguments to
#the function are copied to new variables
#that come into existence when the function
#is called and placed in new variables
#named by the function definition.
#When the function returns, all these
#(temporary) variables are vanished
#with just one exception: arrays.
#Arrays are _passed by reference_,
#but that is a topic to be discussed later.
#Let's see some examples.

 function testVars(x, y,z) {
 	if ((x <= 0) || (y <= 0))
 		return(0)
 	z = sqrt((x * x) + (y * y))
 	if (z > (2 * x))
 		return(z)
 	return(x)
 }

#The above function is defined to be
#passed two (number)
#variables and return either zero, or
#another (number) value, based on some
#tests done on the values of `x` and `y`
#passed.
#If called as

#  testVars(-1, 10)

#the function returns zero (because `x` value is
#less than zero). If called as

#  testVars(3, 4)

#the function returns 3 (because the `z`
#is calculated to  5 and that value is
#less than two times `x`, which is 6,
#thus returning the value of `x`
#which is 3). It's clear that `x` and
#`y` are "local" variables in the
#`testVars` function, that is the names
#`x` and `y` are known in the scope
#of the `testVars` function and have nothing
#to do with other variables named `x` or
#`y` anywhere outside that function.
#The `x` and `y` variables come into existence
#when the function is called and receive as
#values the corresponding values passed
#as function arguments.
#However, that's not true for the `z` variable.
#The `z` variable is not declared as a 
#function argument, so when assigning some
#value to it, the `z` variable comes into
#existence and is globaly known. More, if
#the `z` variable was already a global variable
#when we called the `testVars` function, then
#any value already assigned to (global) `z` variable
#is replaced by the expression calculated inside
#`testVars`.

#How can we avoid the globalness of the
#`z` variable and localize the scope of `z`
#inside the `testVars` function?
#There is a simple solution to that kind
#of problem, due to the freedom of the
#arguments count to be passed in awk functions.
#Actually, we can declare a function to accept
#a specific number of arguments, but we
#can call the function with less arguments than
#those declared in the function's definition!
#That's the key in creating "local" or "static"
#variables to be known only in a function's
#scope. Let the `testVars` function be declared as:

#  function testVars(x, y,		z) {
#  	if ((x <= 0) || (y <= 0))
#  		return(0)
#  	z = sqrt((x * x) + (y * y))
#  	if (z > (2 * x))
#  		return(z)
#  	return(x)
#  }

#Declaring `z` as a function argument
#the `z` variable become "static" in the `testVars`
#function's scope. The above function definition
#states that the `testVars` function accepts three
#arguments (named as `x`, `y` and `z`), but we
#use to call the function with only two of the
#three declared arguments. That simple assumption
#has a simple side effect of declaring local
#`x`, `y` and `z` variables and the two values
#passed when calling the function
#are placed in `x` and `y` variables. We don't pass
#a thrid variable, so the `z` variable is created
#but not take any value (remains empty), but we can
#use `z` variable in the function's code to store some values.

#Discussion
#==========

#The method of creating local (static) function
#variables just by declaring the desired variables
#as (unpassed) function arguments is a very clever
#idea of the awk creators. BTW, when passing arrays
#as function arguments, we must have in mind that
#the array elements are not copied; they just exist
#with the same indices. If any new elements are
#created during the function execution, or some elements
#are deleted, then the array passed is affected, no
#array elements are copied to static variables,
#just the name of the array is to be new.
#To give an example on passing arrays as
#functon arguments we'll develop a function to
#double all the values of the array passed.
#We also add a new element indexed "sum" to
#hold the doubled values' total, and return
#the count of the array elements.

#  function testArr(x,		i, n) {
#  	delete x["sum"]
#  	for (i in x) {
#  		x[i] *= 2
#  		x["sum"] += x[i]
#  		n++
#  	}
#  	return(n)
#  }

#We define the function to accept _three_ values
#as arguments, but `i`, and `n` will not actually
#be passed when calling the function (that's the reason
#to put some extra tabs before `i` and `n`, it's only
#to emphasize the fact that `i` and `n` may not be passed,
#but rather are variables to be used as static in `testArr`).
#We first delete the "sum" indexed element (in case that
#element already exists), and then iterate all elements
#in the array passed using the (local) `i` variable
#as the runing index. We double each element's value
#and add the doubled value to the (recreated)
#`x&#91;"sum"&#93;` element. We also increase the counter `n`,
#also declared as local in `testArr`.

#After visiting all the array elements, we
#return the count of the elements visited.
#However, there is a bug in the function.
#We deleted the `x&#91;sum"&#93;` element in the
#begining of the function, but we recreate the
#element in the first loop pass.
#Doing so, we'll visit that element in some
#future loop pass and thus get erroneus results.
#To fix that bug we declare another local
#variable to hold the total, while the loop is on,
#and create the "sum" indexed element after
#visiting all the array elements:

#  function testArr(x,		i, n, tot) {
#  	delete x["sum"]
#  	for (i in x) {
#  		x[i] *= 2
#  		tot += x[i]
#  		n++
#  	}
#  	x["sum"] = tot
#  	return(n)
#  }

#That's all folks!

#Author
#======

#Panos Papadopoulos

##12345 #####################################

#arrayHTML
#Arrays Sandbox PanosP

#Printing an Array in a Horizontally Columned HTML Table

#Problem
#=======

#We want to print array elements as an HTML table.

#Solution
#========

#That's a straight forward task:

 function arrayTable(arr, tstyle, dstyle,		i, header) {
 	# dstyle is used for styling <td> data (table cells).
 	if (!dstyle)
 		dstyle = "style=\"border-style: inset; " \
			"border-width: 2px;\""
 	for (i in arr) {
 		if (!header) {
 			# tstyle is used for styling the table as a whole.
 			print "<table " (tstyle ? tstyle : \
 				"style=\"border-style: outset; " \
				"border-width: 1px;\"") ">"
 			header = 1
 		}
 		print "\t<tr>"
 		print "\t\t<td " dstyle ">"
 		print i
 		print "\t\t</td>"
 		print "\t\t<td " dstyle ">"
 		print arr[i]
 		print "\t\t</td>"
 		print "\t</tr>"
 	}
 	if (header)
 		print "</table>"
 }

#Discussion
#==========
#Array elements are printed in random order.
#To print an array in an ordered fashion we must
#sort the array and _then_ use a function similar to the
#above function to print the array. Of course, that
#kind of function must iterate a number indexed array.

#The `tstyle` and `dstyle` arguments are optional. If given,
#they will be used as given, e.g.

#  arrayTable(a, "class=\"tbarr\"", "class=\"arrel\"")

#If not given, then a default styling is used. To avoid the default
#styling at all we can supply arguments that contain just a space,
#e.g. to avoid the inner syling (no border for table elements):

#  arrayTable(a, "", " ")

#In the above call we supply a space as the `dstyle` argument,
#causing the `if&nbsp;(!dstyle)` check to fail, while we keep the
#default styling for the table as a whole by supplying an empty
#argument for `tstyle`, causing the `(tstyle&nbsp;?&hellip;` check
#to fail, and thus use the default cell styling.

#Author
#======

#Panos Papadopoulos

